Demos:
Corrections:
Cycling the sprite image.
[See issue #128 for a discussion of changing the sprite image.]
Liberty BASIC has a special command that allows you to cycle through the frames of action of a sprite. This is great when you want to make a sprite look alive. A sprite man can appear to be walking, a fire can appear to be flaming, etc. The command is CYCLESPRITE
You can also use the SPRITEIMAGE command to cycle the sprite through its frames of animation in your code.
Why change the image yourself?
You can change the image yourself, by why would you do this? You might do it because you need more precise control over a sprite. What if your sprite man is walking part of the time, and crawling part of the time? You might have three different images for walking and three others for crawling. If you let Liberty BASIC cycle for you, the man would walk, then crawl, then walk, then crawl. Oops!
You might also need to synchronize other actions with the sprite animation, so knowing which frame (image) is being displayed can be critical. What if your sprite makes a clomping sound as he walks along? You'd want the sound to happen each time his foot hit the ground, of course!
What if you want to change the speed at which a sprite cycles in relation to the cycling of other sprites? You might have a bug that moves its legs quickly, and another that is a slow poke.
Taking control of the frames of animation.
If you want to switch the image yourself, it is easiest to have a variable that holds the index of the current image. If you give the images the right kind of names when you load them, you can cycle quite easily. Notice the loadbmp names of the sprite images:
loadbmp "club1","club1.bmp"
loadbmp "club2","club2.bmp"
loadbmp "club3","club3.bmp"
#main.g "addsprite guy club1 club2 club3"
You can access any of the three images by using a variable, like this:
frame=2
#main.g "spriteimage guy club";frame
The variable is added to the name "club." This is called string concatenation. In the example above, LB sees the command in this way:
#main.g "spriteimage guy club2"
Since the sprite name is actually a string, you can also have an array that holds the names. Here's an example.
guy$(1)="club1"
guy$(2)="club2"
guy$(3)="club3"
#main.g "spriteimage guy ";guy$(2)
Liberty BASIC sees the command like this:
#main.g "spriteimage guy club2"
Don't neglect the blank space. If you forget it like this:
#main.g "spriteimage guy";guy$(2)
Then Liberty BASIC sees this incomprehensible command:
#main.g "spriteimage guyclub2"
How do we make this animation work?
The easiest way to do this is by keeping a counter variable and incrementing it by "1" each time the new frame is displayed with drawsprites. If we call the counter variable frame, we'd change it like this:
frame=frame+1 'advance frame of animation
Can you see how we'd use that with the methods above, either concatenating it with the sprite name, or using it as the index for the sprite array? In the demo, it looks like this:
#main.g "spriteimage guy club";frame
Don't forget to check the value of the counter, and set it back to "1" if it goes over the limit. We have three images, so if the counter has a value greater than 3, we set it back to 1.
if frame>3 then frame=1
Here is part of the main animation loop from the demo. Don't forget to use a SCAN command so that user events can be handled. Using the Sleep API call creates a pause so that the frame of animation stays on the screen for a brief time, and it frees the processor. [See issue #105]
[loop]
scan
frame=frame+1 'advance frame of animation
if frame>3 then frame=1
#main.g "spriteimage guy club";frame
#main.g "drawsprites"
calldll #kernel32, "Sleep", 200 as long, re as void
goto [loop]
Synchronizing Sounds
The demo for this article has our little caveman with the club. We first met him in the last article. This time there is a rock in front of him and he swings his club at it. Each time his club makes contact with the rock, there is a sound effect.

We need to synchronize the sound with the action. If we play a wave with the LOOP mode, the sound will play over and over, but it won't be in synchrony with the action. To synchronize sound and action we'll play a wave ASYNC. That sounds strange! If we want to synchronize the sound, wouldn't we play it SYNC? Nope!
Async vs Sync
Liberty BASIC gives us three ways to play a wave sound. From the helpfile:
PLAYWAVE "filename" [, mode ]
Description:
This plays a *.wav sound from a file on disk as specified in filename. If mode is specified, it must be one of the modes described below:
sync (or synch) - wait for the wave file to finish playing (the default)
async (or asynch) - don't wait for the wave file to finish playing
loop - play the wave file over and over (cancel with: playwave "")
Playing a wave with the SYNCH or SYNC mode causes action to halt while the wave is playing. Although this may be useful sometimes, we don't want our caveman to pause each time he strikes the rock with his club. We'll use the ASYNCH or ASYNC mode so that action can continue as the sound effect plays. The code looks like this:
playwave "hit.wav",async
Since we don't want to play the wave in each frame of animation, we check the value of our frame variable, and only play the wave when the third image is being displayed.
if frame=3 then playwave "hit.wav",async
Here is the entire animation loop, including the incrementation of the counter variable, the changing of the image with spriteimage , the scan command, the Sleep API call, and the wave playing along with the image of the caveman striking the rock with his club, and of course the drawsprites command that causes the display to be updated on the screen:
[loop]
scan
frame=frame+1 'advance frame of animation
if frame>3 then frame=1
#main.g "spriteimage guy club";frame
#main.g "drawsprites"
if frame=3 then playwave "hit.wav",async
calldll #kernel32, "Sleep", 200 as long, re as void
goto [loop]
The demo follows. We can accomplish quite a lot with very little code!
DEMO
Please download the zipped archive of this issue to get the bitmaps and wave file.
[WindowSetup]
NOMAINWIN
WindowWidth = 238 : WindowHeight = 200
UpperLeftX = INT((DisplayWidth-WindowWidth)/2)
UpperLeftY = INT((DisplayHeight-WindowHeight)/2)
[ControlSetup]
graphicbox #main.g, 1, 1, 228, 180
Open "Cycling & Sound" for Window_nf as #main
#main "trapclose [quit]"
#main.g "down; fill brown; flush"
#main.g "getbmp bkg 0 0 228 178"
#main.g "background bkg"
[SpriteSetup]
loadbmp "club1","club1.bmp"
loadbmp "club2","club2.bmp"
loadbmp "club3","club3.bmp"
loadbmp "stone","stone.bmp"
#main.g "addsprite stone stone"
#main.g "spritexy stone 70 80"
#main.g "addsprite guy club1 club2 club3"
#main.g "spritexy guy 85 50"
[loop]
scan
frame=frame+1 'advance frame of animation
if frame>3 then frame=1
#main.g "spriteimage guy club";frame
#main.g "drawsprites"
if frame=3 then playwave "hit.wav",async
calldll #kernel32, "Sleep", 200 as long, re as void
goto [loop]
[quit]
unloadbmp "club1"
unloadbmp "club2"
unloadbmp "club3"
unloadbmp "stone"
close #main : END
'Game artwork created by Ari Feldman ari@arifeldman.com